package cn.juque.lucenecandy.threads;

import cn.hutool.core.thread.ThreadUtil;
import cn.juque.common.constants.NumberConstant;
import cn.juque.lucenecandy.cache.IndexReaderCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.index.IndexReader;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>CloseIndexReaderThreadPool</li>
 * </ul>
 * @date 2023-12-20
 **/
@Slf4j
@Component("indexReaderThreadPool")
public class IndexReaderThreadPool {

    private static final ThreadFactory THREAD_FACTORY = ThreadUtil.newNamedThreadFactory("close-index-", true);

    private static final ScheduledExecutorService EXECUTOR =
            new ScheduledThreadPoolExecutor(2, THREAD_FACTORY, new ThreadPoolExecutor.DiscardPolicy());

    private static final AtomicBoolean CLOSE_END = new AtomicBoolean(true);

    private static final AtomicBoolean CHECK_END = new AtomicBoolean(true);

    @Resource
    private IndexReaderCache indexReaderCache;

    public void init() {
        EXECUTOR.scheduleWithFixedDelay(this::check, 0, 1, TimeUnit.SECONDS);
        EXECUTOR.scheduleAtFixedRate(this::close, 0, 15, TimeUnit.SECONDS);
        log.debug("success init IndexReader ThreadPool");
    }

    private void close() {
        if (!CLOSE_END.compareAndSet(true, false)) {
            return;
        }
        try {
            long count = 0;
            IndexReader indexReader = this.indexReaderCache.pollRecycle();
            while (Objects.nonNull(indexReader)) {
                count++;
                this.close(indexReader);
                indexReader = this.indexReaderCache.pollRecycle();
            }
            if (count > 0) {
                log.debug("this time clear IndexReader count:{}", count);
            }
        } catch (Exception e) {
            log.error("close IndexReader error", e);
        } finally {
            CLOSE_END.set(true);
        }
    }

    private void close(IndexReader indexReader) {
        try {
            indexReader.close();
        } catch (IOException e) {
            log.error("close IndexReader error", e);
        }
    }

    private void check() {
        if (!CHECK_END.compareAndSet(true, false)) {
            return;
        }
        long limit = (long) NumberConstant.FIVE * NumberConstant.ONE_THOUSAND;
        try {
            boolean flag = true;
            Map<IndexReader, Long> map;
            while (flag) {
                map = this.indexReaderCache.pollOld();
                if (Objects.isNull(map)) {
                    flag = false;
                    continue;
                }
                AtomicBoolean success = new AtomicBoolean(false);
                map.forEach((k, v) -> {
                    if (System.currentTimeMillis() - v > limit) {
                        success.set(this.indexReaderCache.offerRecycle(k));
                    }
                });
                // reader在等待回收区没有超过5s，或回收队列已满
                if (!success.get()) {
                    this.indexReaderCache.offerOld(map);
                }
            }
        } catch (Exception e) {
            log.error("check old index queue error", e);
        } finally {
            CHECK_END.set(true);
        }
    }
}
